package mutilthread;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilter;

/**
 * 多线程按目录批量下载
 * 
 * @author zhangping
 * 
 */
public class ZPFTPApp {

	private FTPClient ftp;

	// / 需要下载的文件后缀
	private static String[] _ext = {};

	// / 不需要下载的目录
	private static String[] _not = {};

	// / 本地存储目录
	private static String local_path = "";
	// / 远程下载目录
	private static String path = "";

	// encode ,default GBK
	private static String encoding = "";
	// /username
	private static String username = "";
	// /password
	private static String password = "";
	// / HOST IP
	private static String HOST = "";
	// /fpt port
	// private static int PORT = 21;
	// /下载文件大小限制
	private static long MAX_SIZE = 1;// (1MB)
	// /被动模式
	private static boolean isPASV = false;
	// /是否覆盖
	private static boolean cover = false;
	// /线程池大小
	private static int threads = 1;
	// /线程池
	private ExecutorService pool = null;

	/**
	 * 连接下载fpt服务器
	 * 
	 * @param server
	 * @param port
	 * @param user
	 * @param password
	 * @throws IOException
	 */
	public void connectServer() throws Exception {
		// server：FTP服务器的IP地址；user:登录FTP服务器的用户名
		// password：登录FTP服务器的用户名的口令；path：FTP服务器上的路径
		ftp = getFTPClient();
		if (ftp == null) {
			p("ERROR");
			return;
		}
		for (String s : ftp.getReplyStrings()) {
			System.out.println(s);
		}
	}

	/**
	 * 关闭ftp连接
	 */
	public void closeServer() {
		try {
			ftp.logout();
			if (ftp != null) {
				ftp.disconnect();
				ftp = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 关闭线程池
	 */
	private void shutdownPool() {
		pool.shutdown();
	}

	private FTPClient getFTPClient() throws Exception {
		FTPClient ftpInstance = new FTPClient();
		ftpInstance.connect(HOST);
		ftpInstance.login(username, password);// 登录
		ftpInstance.setControlKeepAliveTimeout(1000);
		ftpInstance.setControlEncoding(encoding);
		if (isPASV) {
			ftpInstance.enterLocalPassiveMode(); // 被动模式
		} else {
			ftpInstance.enterLocalActiveMode(); // 主动模式
		}
		return ftpInstance;
	}

	/**
	 * 初始化参数
	 * 
	 * @param file
	 */
	public void init(String file) {
		Config dao = new Config(file);
		local_path = dao.readValue("local_path", "").trim();
		path = dao.readValue("path", "").trim();
		username = dao.readValue("username", "").trim();
		password = dao.readValue("password", "").trim();
		HOST = dao.readValue("HOST", "").trim();
		encoding = dao.readValue("encoding", "GBK").trim();
		isPASV = dao.readValue("isPASV", "true").trim().equals("true");
		cover = dao.readValue("cover", "false").trim().equals("true");
		threads = Integer.parseInt(dao.readValue("threads", "1").trim()); // /线程池大小
		MAX_SIZE = Long.valueOf(dao.readValue("MAX_SIZE", "1").trim());
		String _ext_string = dao.readValue("_ext", "").trim().replaceAll(" ", "").replaceAll(",,", ",");
		String _not_string = dao.readValue("_not", "").trim().replaceAll(" ", "").replaceAll(",,", ",");

		if (MAX_SIZE < 1) {
			MAX_SIZE = 1;
			p("config[MAX_SIZE] must biger than 0");
		}
		MAX_SIZE = MAX_SIZE * 1024 * 1024; // 转换成MB

		if (threads < 1) {
			threads = 1;
			p("config[threads] must biger than 0");
		}

		if (_ext_string.equals("")) {
			_ext_string = "*";
		}

		_ext = _ext_string.split(",");
		_not = _not_string.split(",");

	}

	/**
	 * 初始化线程池 固定大小
	 */
	private void initThreadPool() {
		pool = Executors.newFixedThreadPool(threads);
	}

	/**
	 * 下载指定目录
	 * 
	 * @param path
	 * @throws IOException
	 */
	public void download(String path) throws Exception {
		path = path.replace("\\", "/").replace("//", "/");
		p(">>>目录:" + path);

		if (notDownloadDir(path)) {
			p("path[" + path + "] is ignore");
			return;
		}

		FTPFile[] fileList = ftp.listFiles(path, new FTPFileFilter() {
			public boolean accept(FTPFile paramFTPFile) {
				try {
					return paramFTPFile.getType() != FTPFile.DIRECTORY_TYPE;
				} catch (RuntimeException e) {
					return false;
				}
			}
		}); // file
		FTPFile[] fileDirectory = ftp.listDirectories(path); // directory

		// / download file
		for (int i = 0; i < fileList.length; i++) {
			ftp_down(path, fileList[i]);
		}

		for (int i = 0; i < fileDirectory.length; i++) {
			download(path + File.separator + fileDirectory[i].getName());
		}

	}

	/**
	 * 建立本地文件所属目录
	 * 
	 * @param local_file
	 */
	private void createPathifNotExists(String local_file) {
		String fs = new File(local_file).getParent();
		File f = new File(fs);
		if (!f.exists()) {
			p("创建目录:" + f.getAbsolutePath());
			f.mkdirs();
		}
	}

	/**
	 * logogram for System.out.println();
	 * 
	 * @param s
	 */
	private void p(String s) {
		System.out.println(s);
	}

	/**
	 * 不需要下载的目录
	 * 
	 * @param path
	 * @return
	 */
	private boolean notDownloadDir(String path) {
		if (_not.length < 1) {
			return false;
		}
		for (int i = 0; i < _not.length; i++) {
			if (path.startsWith(_not[i])) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 是可以下载的文件
	 * 
	 * @param path
	 * @return
	 */
	private static boolean isOkFile(String path) {
		if (_ext[0].trim().equals("*")) {
			return true;
		}
		for (int i = 0; i < _ext.length; i++) {
			if (path.endsWith(_ext[i])) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 本地文件是否存在
	 * 
	 * @param localFile
	 * @return
	 */
	private boolean localFileExists(File localFile) {
		return localFile.exists();
	}

	private void deletelocalFile(File localFile) {
		localFile.deleteOnExit();
	}

	private void ftp_down(String path, FTPFile file) {
		try {
			// full path on server
			String rem_file = (path + File.separator + file.getName()).replace('\\', '/');
			String local_file = (local_path + path + File.separator + file.getName()).replace("\\", "/");

			if (!isOkFile(rem_file)) {
				// p(rem_file + " is not allowed to be download");
				return;
			}

			if (file.getSize() > MAX_SIZE) { // 超过大小
				p(rem_file + "'s size(" + (Double.valueOf(file.getSize() / 1024 / 1024)) + "MB) is more than " + (MAX_SIZE / 1024 / 1024) + "(MB)");
				return;
			}

			if (localFileExists(new File(local_file))) {
				if (cover) {
					deletelocalFile(new File(local_file));
				} else {
					return;
				}
			}

			createPathifNotExists(local_file);

			newThreadDownload(rem_file, local_file);

		} catch (Exception e) {
		}

	}

	private void newThreadDownload(String rem_file, String local_file) {
		pool.execute(new Downloading(rem_file, local_file));
	}

	class Downloading implements Runnable {
		private String rem_file = "";
		private String local_file = "";
		private FTPClient ftp_down = null;

		public Downloading(String rem_file, String local_file) {
			try {
				this.rem_file = rem_file;
				this.local_file = local_file;
				ftp_down = getFTPClient();
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

		public void run() {
			OutputStream out = null;
			try {
				out = new FileOutputStream(local_file);
				ftp_down.retrieveFile(rem_file, out);
				int reply = ftp_down.getReplyCode();
				if (reply == 226) {
					System.out.println(rem_file + " is download: " + local_file);
				} else {
					for (String s : ftp.getReplyStrings()) {
						System.out.println("retrieveFile error:" + s);
					}
				}
			} catch (Exception e) {
				p("path:::" + path);
				p("file:::" + rem_file);
				e.printStackTrace();
			} finally {
				try {
					// Thread.sleep(500);
					out.close();
					ftp_down.logout();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if (args == null || args.length < 1) {
			System.out.println("第一个参数是配置文件");
			System.exit(0);
		}
		if (!new File(args[0]).exists()) {
			System.out.println("文件不存在,请确认路径[" + args[0] + "]");
			System.exit(0);
		}
		
		ZPFTPApp dao = new ZPFTPApp();
		dao.init(args[0]);
		try {
			// 固定大小,线程池
			dao.initThreadPool();
			dao.connectServer();
			dao.download(path);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			dao.closeServer();
			dao.shutdownPool();
		}
	}

}
